i18n 全寫為 internationalization,俗稱的多國語系也常被稱之為本地化 (Localization)。
隨著現在網路的發達和資訊的流通,開始出現許多跨國的服務和產品,這些服務和產品為了推廣到更深或更多的國家,而必須提供當地的語言或者是多數人能讀懂的語言給使用者,也因此製作多國語系的網站似乎漸漸變成前端工程師必備的技能之一,所以我們要介紹的第一個前端實戰技能就是多國語系 i18n。
在 Vue 中要實作多國語系,最常用的套件就是 vue-i18n,接下來為了方便示範,我會使用 Vue CLI 來建置專案,那我們馬上開始吧!
使用 Vue CLI 建置專案 (記得要選 Vue 3.x )。
$ npm install -g @vue/cli
$ vue create [專案名稱]
$ cd [專案名稱]
$ npm run serve
接著只要輸入 vue add i18n,並選擇相關的配置就會開始安裝。
$ vue add i18n
src
directory.安裝完後,會發現 CLI 自動幫你建立或更動了很多檔案,其中值得注意的有:
locales 資料夾 - 用來存放多國語系設定檔的資料夾,於安裝時第三步驟設定的資料夾。
en.json - 在 locales 資料夾底下的多國語系設定檔,其檔案格式為 json。
{
"message": "hello i18n !!"
}
i18n.js - 它會將 locales 資料夾底下所有的語系檔 (.json) 都載入,並 export instance 出去,後續有進階設定時,都會在這支 js 設定。
// i18n.js
import { createI18n } from 'vue-i18n'
/**
* Load locale messages
*
* The loaded `JSON` locale messages is pre-compiled by `@intlify/vue-i18n-loader`, which is integrated into `vue-cli-plugin-i18n`.
* See: https://github.com/intlify/vue-i18n-loader#rocket-i18n-resource-pre-compilation
*/
function loadLocaleMessages () {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
const messages = {}
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
messages[locale] = locales(key).default
}
})
return messages
}
export default createI18n({
legacy: false,
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages: loadLocaleMessages()
})
main.js - 全局載入 i18n,這樣整個專案都能共用一樣的設定以及 locales 底下的語系檔。
import { createApp } from 'vue'
import App from './App.vue'
import i18n from './i18n'
createApp(App).use(router).use(i18n).mount('#app')
自動生成的 HelloI18n.vue 因為後續不會用到,所以可以先刪除 ~
紅色框框起來的是下面要示範的部分
現在要做的第一件事也是最重要的事就是先定義好語系檔 (.json),格式是蠻單純物件的 Key 和 Value,Key 值是待會會寫到程式碼中的值,而 Value 就是呈現在畫面中的值,而我們也可以根據語意或是將畫面相近的文字內容用一層一層的物件來分類。
// src/locales/en.json
{
"subTitle": {
"installedCliPlugins": "Install CLI Plugins",
"essentialLinks": "Essential Links",
"ecosystem": "Ecosystem"
},
"installedCliPlugins": {
"babel": "babel",
"eslint": "eslint"
},
...
}
接著就是開始一個蘿蔔一個坑用 t(key, locale) 替換掉原先 hardcode 的文字就大功告成了,其中的兩個參數分別是:
key
- 在語系檔中 key 。locale
- 指定要用哪一個語系來本地化,預設會是全局的 locale 值。// src/components/HelloWorld.vue
<template>
<div class="hello">
...
<h3>{{ t("subTitle.installedCliPlugins") }}</h3>
<ul>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
target="_blank"
rel="noopener"
>
{{ t("installedCliPlugins.babel") }}
</a>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
target="_blank"
rel="noopener"
>
{{ t("installedCliPlugins.eslint") }}
</a>
</li>
</ul>
...
</div>
</template>
<script>
import { useI18n } from 'vue-i18n'
export default {
name: 'HelloWorld',
props: {
msg: String
},
setup () {
const { t } = useI18n()
return {
t
}
}
}
</script>
講了半天終於要來實作 "多" 語系的部分了,不然前面好像只是換個方式寫字串而已 XD
首先在 locales 底下新增專案所需支援的語系的語系檔 (.json),所以現在我就新增一份 zh.json 來存放切換到繁體中文時要顯示內容,Key 會維持不變,Value 的部分就是看在該語系想要呈現什麼文字內容。(請容許我無腦的用 Google 翻譯中文)
// src/locales/zh.json
{
"subTitle": {
"installedCliPlugins": "已安裝的 CLI 插件",
"essentialLinks": "基本鏈接",
"ecosystem": "生態系統"
},
"installedCliPlugins": {
"babel": "babel",
"eslint": "eslint"
},
...
}
接著透過 useI18n() 來取得 locale ,locale 的值會是當前語系的值,如果改變它的值就會切換當前的語系,所以我們簡單的透過 v-model + select 來實現了切換語系的功能。
// src/components/HelloWorld.vue
<template>
<div class="hello">
...
<select v-model="locale">
<option
v-for="item in localeOptions"
:key="`locale-${item.lang}`"
:value="item.lang"
>
{{ item.name }}
</option>
</select>
<h3>{{ t("subTitle.installedCliPlugins") }}</h3>
...
</div>
</template>
<script>
import { useI18n } from 'vue-i18n'
export default {
...
setup () {
const { t, locale } = useI18n()
return {
t,
locale,
localeOptions: [
{
lang: 'en',
name: 'English'
},
{
lang: 'zh',
name: '繁體中文'
}
]
}
}
}
</script>
由於每次要將文字本地化都要先在 setup 透過 useI18n() 取得 t(),可是假如有 100 個 component 都要使用 t() 那豈不是要重複寫 100 次一樣的程式碼片段 ?
<script>
import { useI18n } from 'vue-i18n'
export default {
setup () {
const { t } = useI18n()
return {
t
}
}
}
</script>
還好 vue i18n 有提供一個配置選項 globalInjection,設為 true 時它會將 t 全局注入至 component 中,所以我們只要多加一個 $ 前綴符號就能直接在 template 中使用了
// src/i18n.js
...
export default createI18n({
...
globalInjection: true // <------ 加上這一行
})
// src/components/HelloWorld.vue
<template>
<div class="hello">
...
<h3>{{ $t("subTitle.installedCliPlugins") }}</h3>
...
</div>
</template>
事實上將 globalInjection 設為 true 時,還會有其他的屬性被注入至 component 中。
今天的分享就到這邊,如果大家對我分享的內容有興趣歡迎點擊追蹤 & 訂閱系列文章,如果對內容有任何疑問,或是文章內容有錯誤,都非常歡迎留言討論或指教的!
明天要來分享的是 Vue i18n 主題的第二篇 Message Format Syntax,那我們明天見!
? The directory where store localization messages of project. Its stored undersrc
directory.
這裡的排版格式可以稍微調整一下 :)
感謝 TD !!! 從 Notion 搬過來的時候沒注意到!
還好 vue i18n 有提供一個配置選項 globalInjection,設為 true 時它會將 t 全局注入至 component 中,所以我們只要多加一個 $ 前綴符號就能直接在 template 中使用了
我對 Vue 不熟 ? 不過這裡的意思是我們只要在 root 設定完 (取出 t
) 之後,在其他所有的 component 當中只要使用 $t
就可以取用的意思嗎?
TD 沒錯!
我們只要在 i18n 的設定檔中將 globalInjection 設為 true,在其他所有 component 的 template 中就可以直接使用 $t
取用了
請問上面一切都沒問題,但只要在json內輸入@ . !^%#$%&#符號就出錯
json內拿掉符號就沒問題,有符號就出錯
在i18n.js 改了以下還是出錯,
//const matched = key.match(/([A-Za-z0-9-]+)./i)
const matched = key.match(//([a-z]{2,3}).json$/i)//
請問有沒有什麼方式可解決呢?
謝謝
大大
我看到你的下一篇了,順利解決了
一開始以為是i18n i18nloader問題(嘗試解決都沒用)
後來看到你的下一篇有提到Special Characters進階格式與法要求了
https://ithelp.ithome.com.tw/articles/10266493
非常感謝你的分享^^